package com.zhiyou.app.shop.manager.dao.impl;

import java.io.Serializable;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.hibernate.Criteria;
import org.hibernate.HibernateException;
import org.hibernate.Session;
import org.hibernate.criterion.DetachedCriteria;
import org.hibernate.criterion.Example;
import org.hibernate.criterion.Order;
import org.hibernate.criterion.Projections;
import org.hibernate.criterion.Restrictions;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.orm.hibernate4.HibernateCallback;
import org.springframework.orm.hibernate4.HibernateTemplate;
import org.springframework.stereotype.Repository;

import com.zhiyou.app.shop.common.util.Page;
import com.zhiyou.app.shop.manager.dao.IBaseDAO;
import com.zhiyou.app.shop.website.util.SiteUtil;


/**
 * Data access object (DAO) for domain model
 * @author kewei
 */
@Repository
public class BaseHibernateDAO<T , PK extends Serializable> implements IBaseDAO<T , PK>{
	
	private static Log log = LogFactory.getLog(BaseHibernateDAO.class);
	@Autowired
	public HibernateTemplate hibernateTemplate;
	
	private Class<T> type;

	public BaseHibernateDAO(){}
	
	/**
	 * @param type
	 */
	public BaseHibernateDAO(Class<T> type) {
		super();
		this.type = type;
	}

	
	/**
	 * 保存po对象(该方法处于一个单独的事物中)
	 * @param transientInstance
	 * @throws DAOException
	 */
	public void save(T transientInstance){
		log.debug("save "+transientInstance.getClass().getName()+" instance");
		hibernateTemplate.saveOrUpdate(transientInstance);
		log.debug("save successful");
	}
	/**
	 * 删除po对象(该方法处于一个单独的事物中)
	 * @param persistentInstance
	 * @throws DAOException
	 */
	public void delete(T persistentInstance){
		log.debug("delet "+persistentInstance.getClass().getName()+"  instance");
		hibernateTemplate.delete(persistentInstance);
	}
	
	public void saveOrUpdate(T transientInstance){
		log.debug("save update"+transientInstance.getClass().getName()+" instance");
		hibernateTemplate.saveOrUpdate(transientInstance);
		log.debug("save or update successful");
	}
	/**
	 * 修改po对象(该方法处于一个单独的事物中)
	 * @param transientInstance
	 * @throws DAOException
	 */
	public void update(T transientInstance){
		log.debug("updat "+transientInstance.getClass().getName()+" instance");
		hibernateTemplate.update(transientInstance);
		log.debug("update successful");
	}
	
	
	public T findById(PK pk) {
		log.debug("find instance by id: " + pk);
		return (T) hibernateTemplate.get(type, pk);
	}
	
	
	public List<T> findAll(){
		return this.findAll(null);
	}
	
	
	public List<T> findAll(final Order ... order) {
		List<T> list = hibernateTemplate.execute(new HibernateCallback<List<T>>() {

			public List<T> doInHibernate(Session session) throws HibernateException{
				Criteria cri = session.createCriteria(type.getName());
				SiteUtil.addOrderToCriteria(cri, order);
				return cri.list();
			}
			
		});
		
		return list;
	}
	
	public List<T> findByExample(T t) {
		
		return this.findByExample(t, null);
		
	}
	
	public List<T> findByExample(final T t , final Order ... order) {
		
		log.debug("find instance by example");
		List<T> list = hibernateTemplate.execute(new HibernateCallback<List<T>>() {

			public List<T> doInHibernate(Session session) throws HibernateException {
				Criteria cri = session.createCriteria(t.getClass().getName());
				
				cri = cri.add(Example.create(t));
				
				SiteUtil.addOrderToCriteria(cri, order);
				
				List results = cri.list();
				
				log.debug("find by example successful, result size: "+ results.size());
				return results;
			}
			
		});
		
		return list;
	}

	public void findByExample(final Page page , final T t , final Order ... order) {
		hibernateTemplate.execute(new HibernateCallback() {
			public Object doInHibernate(Session session) throws HibernateException{
				log.debug("find instance by example");
				
				Criteria cri = session.createCriteria(t.getClass().getName());
				
				cri = cri.add(Example.create(t));
				
				SiteUtil.setPageProperties(page , cri);
				
				SiteUtil.addOrderToCriteria(cri, order);
				
				List<T> results = cri.list();
				
				page.setResult(results);
				
				log.debug("find by example successful, result size: "+ results.size());
				return null;
			}
			
		});
	}

	public List<T> findByCriteria(DetachedCriteria dCri , Order ... order) {
		log.debug("finding instance by DetachedCriteria");
		Criteria cri = dCri.getExecutableCriteria(hibernateTemplate.getSessionFactory().getCurrentSession());
		SiteUtil.addOrderToCriteria(cri, order);
		return cri.list();
	}
	
	
	public void findByCriteria(Page page , DetachedCriteria dCri , Order ... order) {
		log.debug("finding instance by DetachedCriteria");
		setPageTotalCountOnly(page , dCri);
		Criteria cri = dCri.getExecutableCriteria(hibernateTemplate.getSessionFactory().getCurrentSession());
		addOrderToCriteria(cri, order);
		cri.setFirstResult(page.getOffset()).setMaxResults(page.getPageSize());
		List results = cri.list();
		page.setResult(results);
	}
	
	public void findByCriteria(Page page , DetachedCriteria dCri , List<Order> order) {
log.debug("finding instance by DetachedCriteria");
		
		setPageTotalCountOnly(page , dCri);
		
		Criteria cri = dCri.getExecutableCriteria(hibernateTemplate.getSessionFactory().getCurrentSession());
		
		if(null != order){
			
			for(Order o : order){
				cri.addOrder(o);
			}
		}
		
		cri.setFirstResult(page.getOffset()).setMaxResults(page.getPageSize());
		
		List results = cri.list();
		
		page.setResult(results);
		
	}
	
	public List<T> findByProperty(String propertyName, Object value){
		
		return this.findByProperty(propertyName, value, null);
	}
	
	
	
	@SuppressWarnings("unchecked")
	public List<T> findByProperty(final String propertyName, final Object value , final Order ... order) {
		
		log.debug("finding instance with property: " + propertyName + ", value: " + value);
		List<T> list = hibernateTemplate.execute(new HibernateCallback<List<T>>() {

			public List<T> doInHibernate(Session session) throws HibernateException{
					Criteria cri = session.createCriteria(type.getName());
					
					cri.add(Restrictions.eq(propertyName, value));
					
					SiteUtil.addOrderToCriteria(cri, order);
					
					return cri.list();
			}
		});
		return list;
	}
	/**
	 * @param orderMap
	 * @return
	 */
	private void addOrderToCriteria(Criteria cri , Order ... orders) {
		if(null != orders){
			for(Order o : orders){
				cri.addOrder(o);
			}
		}
	}
	
	/**
	 * @param page
	 * @param cri
	 */
	private void setPageProperties(Page page, Criteria cri){
		
		if(page == null) return;
		
		
		/*获取总记录数开始*/
		Object totalCountObject = cri.setProjection(Projections.rowCount()).uniqueResult();
		
		int totalCount  = totalCountObject ==null ? 0 :Integer.valueOf(totalCountObject.toString());
		
		page.setTotalCount(totalCount);
		/*获取总记录数结束*/	
		
		cri.setProjection(null);
		
		cri.setFirstResult(page.getOffset()).setMaxResults(page.getPageSize());
	}
	
	private void setPageTotalCountOnly(Page page, DetachedCriteria dCri){
		Criteria cri = dCri.getExecutableCriteria(hibernateTemplate.getSessionFactory().getCurrentSession());
		
		this.setPageProperties(page, cri);
	}
	
	protected String getOrderSql(Map<String, String> orderMap) {
		String orderSql="";
		if(orderMap!=null && orderMap.size()>0){
			Iterator<String> it = orderMap.keySet().iterator();
			orderSql=" order by ";
			int i=0;
			while(it.hasNext()){
				i++;
				String key = it.next();
				orderSql+=key+" "+orderMap.get(key);
				if(i>1 && i<orderMap.size()){
					orderSql+=" , ";
				}
			}
		}
		return orderSql;
	}
	/**基类调用此方法获取日志接口**/
	protected Log getLog(){
		return LogFactory.getLog(this.getClass());
	}
	
}